Osvojte si základné koncepty a pokročilé techniky renderingu tieňov v reálnom čase vo WebGL. Táto príručka pokrýva shadow mapping, PCF, CSM a riešenia bežných artefaktov.
WebGL Shadow Mapping: Komplexný sprievodca renderingom v reálnom čase
Vo svete 3D počítačovej grafiky prispieva len málo prvkov k realizmu a imerzii viac ako tiene. Poskytujú zásadné vizuálne podnety o priestorových vzťahoch medzi objektmi, umiestnení svetelných zdrojov a celkovej geometrii scény. Bez tieňov môžu 3D svety pôsobiť plocho, odpojene a umelo. Pre webové 3D aplikácie využívajúce WebGL je implementácia vysokokvalitných tieňov v reálnom čase charakteristickým znakom profesionálnych zážitkov. Táto príručka poskytuje hlboký ponor do najzákladnejšej a najpoužívanejšej techniky na dosiahnutie tohto cieľa: Shadow Mapping.
Či už ste skúsený grafický programátor alebo webový vývojár, ktorý preniká do tretieho rozmeru, tento článok vás vybaví znalosťami na pochopenie, implementáciu a riešenie problémov s tieňmi v reálnom čase vo vašich projektoch WebGL. Vydáme sa na cestu od základnej teórie k praktickým detailom implementácie, preskúmame bežné úskalia a pokročilé techniky používané v moderných grafických enginoch.
Kapitola 1: Základy Shadow Mapping
Shadow mapping je vo svojej podstate inteligentná a elegantná technika, ktorá určuje, či je bod v scéne v tieni, a to tak, že si položí jednoduchú otázku: "Vidí tento bod svetelný zdroj?" Ak je odpoveď nie, znamená to, že niečo blokuje svetlo a bod musí byť v tieni. Na programové zodpovedanie tejto otázky používame dvojfázový rendering.
Čo je Shadow Mapping? Základný koncept
Celá technika sa točí okolo dvojnásobného renderingu scény, zakaždým z iného uhla pohľadu:
- Fáza 1: Hĺbková fáza (perspektíva svetla). Najprv renderujeme celú scénu z presnej pozície a orientácie svetelného zdroja. V tejto fáze sa však nestaráme o farby alebo textúry. Jediná informácia, ktorú potrebujeme, je hĺbka. Pre každý renderovaný objekt zaznamenávame jeho vzdialenosť od svetelného zdroja. Táto kolekcia hodnôt hĺbky je uložená v špeciálnej textúre nazývanej shadow mapa alebo hĺbková mapa. Každý pixel v tejto mape predstavuje vzdialenosť k najbližšiemu objektu z pohľadu svetla v špecifickom smere.
- Fáza 2: Fáza scény (perspektíva kamery). Ďalej renderujeme scénu tak, ako by sme to normálne robili, z pohľadu hlavnej kamery. Ale pre každý jeden kreslený pixel vykonávame dodatočný výpočet. Určíme pozíciu tohto pixelu v 3D priestore a potom sa spýtame: "Aká ďaleko je tento bod od svetelného zdroja?" Potom porovnáme túto vzdialenosť s hodnotou uloženou v našej shadow mape (z fázy 1) na zodpovedajúcom mieste.
Logika je jednoduchá:
- Ak je aktuálna vzdialenosť pixelu od svetla väčšia ako vzdialenosť uložená v shadow mape, znamená to, že pozdĺž tej istej línie pohľadu je iný objekt bližšie k svetlu. Preto je aktuálny pixel v tieni.
- Ak je vzdialenosť pixelu menšia alebo rovná vzdialenosti v shadow mape, znamená to, že nič neblokuje a pixel je plne osvetlený.
Nastavenie scény
Na implementáciu shadow mapping vo WebGL potrebujete niekoľko kľúčových komponentov:
- Svetelný zdroj: Môže to byť smerové svetlo (ako slnko), bodové svetlo (ako žiarovka) alebo reflektor. Typ svetla určí druh projekčnej matice použitej počas hĺbkovej fázy.
- Framebuffer Object (FBO): WebGL normálne renderuje do predvoleného framebufferu obrazovky. Na vytvorenie našej shadow mapy potrebujeme renderovací cieľ mimo obrazovky. FBO nám umožňuje renderovať do textúry namiesto obrazovky. Náš FBO bude nakonfigurovaný s prílohou hĺbkovej textúry.
- Dve sady shaderov: Budete potrebovať jeden shaderový program pre hĺbkovú fázu (veľmi jednoduchý) a druhý pre finálnu fázu scény (ktorá bude obsahovať logiku výpočtu tieňov).
- Matice: Budete potrebovať štandardné matice modelu, pohľadu a projekcie pre kameru. Zásadné je, že budete potrebovať aj maticu pohľadu a projekcie pre svetelný zdroj, často kombinované do jednej "matice svetelného priestoru".
Kapitola 2: Dvojfázový renderingový pipeline podrobne
Rozoberme si dva renderingové fázy krok za krokom, so zameraním na úlohy matíc a shaderov.
Fáza 1: Hĺbková fáza (z pohľadu svetla)
Cieľom tejto fázy je naplniť našu hĺbkovú textúru. Tu je postup:
- Priradenie FBO: Pred kreslením inštruujete WebGL, aby renderoval do vášho vlastného FBO namiesto plátna.
- Konfigurácia viewportu: Nastavte rozmery viewportu tak, aby zodpovedali veľkosti textúry shadow mapy (napr. 1024x1024 pixelov).
- Vyčistenie hĺbkového buffera: Uistite sa, že hĺbkový buffer FBO je vyčistený pred renderingom.
- Vytvorenie matíc svetla:
- Matica pohľadu svetla: Táto matica transformuje svet do pohľadu svetla. Pre smerové svetlo sa táto matica zvyčajne vytvára pomocou funkcie `lookAt`, kde "oko" je pozícia svetla a "cieľ" je smer, ktorým smeruje.
- Matica projekcie svetla: Pre smerové svetlo, ktoré má paralelné lúče, sa používa ortografická projekcia. Pre bodové svetlá alebo reflektory sa používa perspektívna projekcia. Táto matica definuje objem v priestore (krabicu alebo zrezaný kužeľ), ktorý bude vrhať tiene.
- Použite shaderový program hĺbky: Toto je minimálny shader. Jedinou úlohou vertex shaderu je vynásobiť pozíciu vrcholu maticami pohľadu a projekcie svetla. Fragment shader je ešte jednoduchší: zapisuje iba hodnotu hĺbky fragmentu (jeho z-súradnicu) do hĺbkovej textúry. V modernom WebGL dokonca ani nepotrebujete vlastný fragment shader, pretože FBO je možné nakonfigurovať tak, aby automaticky zachytával hĺbkový buffer.
- Renderovanie scény: Nakreslite všetky objekty vrhajúce tiene vo vašej scéne. FBO teraz obsahuje našu kompletnú shadow mapu.
Fáza 2: Fáza scény (z pohľadu kamery)
Teraz renderujeme finálny obrázok pomocou shadow mapy, ktorú sme práve vytvorili, na určenie tieňov.
- Odviazanie FBO: Prepnutie späť na renderovanie do predvoleného framebufferu plátna.
- Konfigurácia viewportu: Nastavte viewport späť na rozmery plátna.
- Vyčistenie obrazovky: Vyčistite farebný a hĺbkový buffer plátna.
- Použite shaderový program scény: Tu sa deje mágia. Tento shader je zložitejší.
- Vertex Shader: Tento shader musí robiť dve veci. Po prvé, vypočíta finálnu pozíciu vrcholu pomocou matíc modelu, pohľadu a projekcie kamery ako obvykle. Po druhé, musí tiež vypočítať pozíciu vrcholu z pohľadu svetla pomocou matice svetelného priestoru z fázy 1. Táto druhá súradnica sa prenáša do fragment shaderu ako varying.
- Fragment Shader: Toto je jadro logiky tieňov. Pre každý fragment:
- Prijmite interpolovanú pozíciu vo svetelnom priestore z vertex shaderu.
- Vykonajte perspektívne delenie na tejto súradnici (vydeľte x, y, z hodnotou w). Tým sa transformuje na Normalizované súradnice zariadenia (NDC), v rozsahu od -1 do 1.
- Transformujte NDC na textúrové súradnice (ktoré sú v rozsahu od 0 do 1), aby sme mohli vzorkovať našu shadow mapu. Toto je jednoduchá operácia škálovania a posunu: `texCoord = ndc * 0.5 + 0.5;`.
- Použite tieto textúrové súradnice na vzorkovanie textúry shadow mapy vytvorenej v fáze 1. To nám dá `depthFromShadowMap`.
- Aktuálna hĺbka fragmentu z pohľadu svetla je jeho z-komponent z transformovanej súradnice svetelného priestoru. Nazvime to `currentDepth`.
- Porovnajte hĺbky: Ak `currentDepth > depthFromShadowMap`, fragment je v tieni. Budeme musieť pridať malé bias do tejto kontroly, aby sme sa vyhli artefaktu nazývanému "shadow acne", o ktorom budeme diskutovať neskôr.
- Na základe porovnania určite faktor tieňa (napr. 1.0 pre osvetlené, 0.3 pre zatienené).
- Použite tento faktor tieňa na výpočet finálnej farby (napr. vynásobte zložky okolitého a difúzneho osvetlenia faktorom tieňa).
- Renderovanie scény: Nakreslite všetky objekty v scéne.
Kapitola 3: Bežné problémy a riešenia
Implementácia základného shadow mapping rýchlo odhalí niekoľko bežných vizuálnych artefaktov. Pochopenie a oprava týchto artefaktov je kľúčové pre dosiahnutie vysokokvalitných výsledkov.
Shadow Acne (Artefakty samozatmavenia)
Problém: Môžete vidieť zvláštne, nesprávne vzory tmavých čiar alebo vzory podobné Moiré na povrchoch, ktoré by mali byť plne osvetlené. Toto sa nazýva "shadow acne". Vyskytuje sa to preto, že hodnota hĺbky uložená v shadow mape a hodnota hĺbky vypočítaná počas fázy scény sú pre ten istý povrch. Kvôli nepresnostiam pohyblivej desatinnej čiarky a obmedzenému rozlíšeniu shadow mapy môžu malé chyby spôsobiť, že fragment nesprávne určí, že je za sebou samým, čo vedie k samozatmaveniu.
Riešenie: Depth Bias. Najjednoduchším riešením je zaviesť malé bias do `currentDepth` pred porovnaním. Tým, že fragment bude vyzerať o niečo bližšie k svetlu, ako v skutočnosti je, vytlačíme ho "von" z vlastného tieňa.
float shadow = currentDepth > depthFromShadowMap + bias ? 0.3 : 1.0;
Peter Panning
Problém: Tento artefakt, pomenovaný po postave, ktorá vedela lietať a stratila svoj tieň, sa prejavuje ako viditeľná medzera medzi objektom a jeho tieňom. Vďaka tomu sa objekty zdajú byť plávajúce alebo odpojené od povrchov, na ktorých by mali spočívať. Je to priamy dôsledok použitia príliš veľkého depth bias.
Riešenie: Slope-Scale Depth Bias. Robustnejším riešením ako konštantné bias je, aby bias závisel od strmosti povrchu vzhľadom na svetlo. Strmšie polygóny sú náchylnejšie na akné a vyžadujú si väčšie bias. Plochejšie polygóny potrebujú menšie bias. Väčšina grafických API, vrátane WebGL, poskytuje funkcie na automatické použitie tohto druhu bias počas hĺbkovej fázy, čo je všeobecne výhodnejšie ako manuálne bias vo fragment shadere.
Perspective Aliasing (Zubaté okraje)
Problém: Okraje vašich tieňov vyzerajú blokovo, zubaté a pixelované. Toto je forma aliasingu. Deje sa to preto, že rozlíšenie shadow mapy je konečné. Jeden pixel (alebo texel) v shadow mape môže pokrývať veľkú oblasť na povrchu v finálnej scéne, najmä pre povrchy v blízkosti kamery alebo tie, ktoré sú zobrazené pod ostrým uhlom. Táto nezhoda v rozlíšení spôsobuje charakteristický blokový vzhľad.
Riešenie: Zvýšenie rozlíšenia shadow mapy (napr. z 1024x1024 na 4096x4096) môže pomôcť, ale má to výrazné náklady na pamäť a výkon a plne to nevyrieši základný problém. Skutočné riešenia spočívajú v pokročilejších technikách.
Kapitola 4: Pokročilé techniky Shadow Mapping
Základné shadow mapping poskytuje základ, ale profesionálne aplikácie používajú sofistikovanejšie algoritmy na prekonanie jeho obmedzení, najmä aliasingu.
Percentage-Closer Filtering (PCF)
PCF je najbežnejšia technika na zjemnenie okrajov tieňov a zníženie aliasingu. Namiesto toho, aby PCF odobral jednu vzorku zo shadow mapy a urobil binárne rozhodnutie (v tieni alebo mimo tieňa), odoberá viaceré vzorky z oblasti okolo cieľovej súradnice.
Koncept: Pre každý fragment vzorkujeme shadow mapu nielen raz, ale vo vzore mriežky (napr. 3x3 alebo 5x5) okolo projektovanej textúrovej súradnice fragmentu. Pre každú z týchto vzoriek vykonávame porovnanie hĺbky. Finálna hodnota tieňa je priemer všetkých týchto porovnaní. Napríklad, ak sú 4 z 9 vzoriek v tieni, fragment bude 4/9 zatienený, čo vedie k hladkej polotieni (mäkký okraj tieňa).
Implementácia: Toto sa robí výlučne v rámci fragment shaderu. Zahŕňa slučku, ktorá iteruje cez malé jadro, vzorkuje shadow mapu v každom posune a akumuluje výsledky. WebGL 2 ponúka hardvérovú podporu (`texture` s `sampler2DShadow`), ktorá môže vykonávať porovnanie a filtrovanie efektívnejšie.
Výhoda: Drasticky zlepšuje kvalitu tieňov nahradením tvrdých, aliasingovaných okrajov hladkými, mäkkými okrajmi.
Náklady: Výkon sa znižuje s počtom vzoriek odobratých na fragment.
Cascaded Shadow Maps (CSM)
CSM je priemyselný štandard pre renderovanie tieňov z jedného smerového svetelného zdroja (ako je slnko) na veľmi veľkej scéne. Priamo rieši problém perspektívneho aliasingu.
Koncept: Hlavnou myšlienkou je, že objekty blízko kamery potrebujú oveľa vyššie rozlíšenie tieňa ako objekty vzdialené. CSM rozdeľuje zrezaný kužeľ pohľadu kamery na niekoľko sekcií alebo "kaskád" pozdĺž jeho hĺbky. Pre každú kaskádu sa potom renderuje samostatná, vysokokvalitná shadow mapa. Kaskáda najbližšie ku kamere pokrýva malú oblasť svetového priestoru a má tak veľmi vysoké efektívne rozlíšenie. Kaskády ďalej pokrývajú postupne väčšie oblasti s rovnakou veľkosťou textúry, čo je prijateľné, pretože tieto detaily sú pre hráča menej viditeľné.
Implementácia: Toto je výrazne zložitejšie.
- V CPU rozdeľte zrezaný kužeľ kamery na 2-4 kaskády.
- Pre každú kaskádu vypočítajte ortografickú projekčnú maticu tesne priliehajúcu k svetlu, ktorá dokonale obklopuje túto časť zrezaného kužeľa.
- V renderingovej slučke vykonajte hĺbkovú fázu viackrát – raz pre každú kaskádu, renderovanie do inej shadow mapy (alebo oblasť textúrového atlasu).
- Vo fragment shadere finálnej fázy scény určte, do ktorej kaskády patrí aktuálny fragment na základe jeho vzdialenosti od kamery.
- Vzorkujte shadow mapu príslušnej kaskády na výpočet tieňa.
Výhoda: Poskytuje konzistentne vysoké rozlíšenie tieňov na obrovské vzdialenosti, vďaka čomu je ideálny pre vonkajšie prostredia.
Variance Shadow Maps (VSM)
VSM je ďalšia technika na vytváranie mäkkých tieňov, ale využíva odlišný prístup ako PCF.
Koncept: Namiesto ukladania iba hĺbky v shadow mape ukladá VSM dve hodnoty: hĺbku (prvý moment) a hĺbku na druhú (druhý moment). Tieto dve hodnoty nám umožňujú vypočítať rozptyl rozdelenia hĺbky. Pomocou matematického nástroja nazývaného Čebyševova nerovnosť potom môžeme odhadnúť pravdepodobnosť, že je fragment v tieni. Kľúčovou výhodou je, že VSM textúru je možné rozmazať pomocou štandardného hardvérovo akcelerovaného lineárneho filtrovania a mipmappingu, čo je matematicky neplatné pre štandardnú hĺbkovú mapu. To umožňuje veľmi veľké, mäkké a hladké polotiene tieňov s pevnými nákladmi na výkon.
Nevýhoda: Hlavnou slabinou VSM je "prekrvenie svetla", kde sa môže zdať, že svetlo preniká cez objekty v situáciách s prekrývajúcimi sa prekážkami, pretože štatistická aproximácia sa môže zrútiť.
Kapitola 5: Praktické tipy na implementáciu a výkon
Výber rozlíšenia vašej shadow mapy
Rozlíšenie vašej shadow mapy je priamy kompromis medzi kvalitou a výkonom. Väčšia textúra poskytuje ostrejšie tiene, ale spotrebuje viac video pamäte a trvá dlhšie, kým sa vykreslí a vzorkuje. Bežné veľkosti zahŕňajú:
- 1024x1024: Dobrý základ pre mnoho aplikácií.
- 2048x2048: Ponúka citeľné zlepšenie kvality pre desktopové aplikácie.
- 4096x4096: Vysoká kvalita, často sa používa pre hrdinské prvky alebo v enginoch s robustným orezávaním.
Optimalizácia zrezaného kužeľa svetla
Aby ste vyťažili maximum z každého pixelu vo vašej shadow mape, je dôležité, aby bol projekčný objem svetla (jeho ortografická krabica alebo perspektívny zrezaný kužeľ) čo najtesnejšie prispôsobený prvkom scény, ktoré potrebujú tiene. Pre smerové svetlo to znamená prispôsobiť jeho ortografickú projekciu tak, aby obsahovala iba viditeľnú časť zrezaného kužeľa kamery. Akýkoľvek nevyužitý priestor v shadow mape je premárnené rozlíšenie.
WebGL rozšírenia a verzie
WebGL 1 vs. WebGL 2: Zatiaľ čo shadow mapping je možné vo WebGL 1, je to oveľa jednoduchšie a efektívnejšie vo WebGL 2. WebGL 1 vyžaduje rozšírenie `WEBGL_depth_texture` na vytvorenie hĺbkovej textúry. WebGL 2 má túto funkciu zabudovanú. Okrem toho WebGL 2 poskytuje prístup k vzorkovačom tieňov (`sampler2DShadow`), ktoré môžu vykonávať hardvérovo akcelerovaný PCF, čo ponúka výrazné zvýšenie výkonu oproti manuálnym PCF slučkám v shadere.
Ladenie tieňov
Tiene sa dajú notoricky ťažko ladiť. Jedinou najužitočnejšou technikou je vizualizovať shadow mapu. Dočasne upravte svoju aplikáciu tak, aby renderovala hĺbkovú textúru z konkrétneho svetelného zdroja priamo na štvoruholník na obrazovke. To vám umožní presne vidieť, čo svetlo "vidí". To môže okamžite odhaliť problémy s maticami svetla, orezávaním zrezaného kužeľa alebo renderovaním objektov počas hĺbkovej fázy.
Záver
Shadow mapping v reálnom čase je základným kameňom modernej 3D grafiky, ktorý transformuje ploché, bezživotné scény na uveriteľné a dynamické svety. Hoci je koncept renderovania z perspektívy svetla jednoduchý, dosiahnutie vysokokvalitných výsledkov bez artefaktov si vyžaduje hlboké pochopenie základnej mechaniky, od dvojfázového pipeline až po nuansy hĺbkového bias a aliasingu.
Začatím so základnou implementáciou môžete postupne riešiť bežné artefakty, ako sú shadow acne a zubaté okraje. Odtiaľ môžete pozdvihnúť svoj vizuál pomocou pokročilých techník, ako je PCF pre mäkké tiene alebo Cascaded Shadow Maps pre rozsiahle prostredia. Cesta do renderovania tieňov je dokonalým príkladom prelínania umenia a vedy, vďaka ktorému je počítačová grafika taká pútavá. Odporúčame vám experimentovať s týmito technikami, posúvať ich hranice a priniesť novú úroveň realizmu do vašich WebGL projektov.